//=============================================================================
/**
 * @file	gym_almighty.c
 * @brief	万能ジムギミック
 * @author	Nozomu Saito
 *
 * モジュール名：GYM_
 */
//=============================================================================

#include "common.h"
#include "fieldsys.h"
#include "fieldmap_work.h"
#include "system/snd_tool.h"
#include "include/savedata/gimmickwork.h"
#include "../fielddata/build_model/build_model_id.h"

#include "field_gimmick_def.h"
#include "sysflag_def.h"

#include "gym_def.h"

#include "gym_local.h"
#include "gym_init.h"

#include "gym.h"

#include "fielddata/mapattr/map_attr.h"

#define ALM_FLOOR_ANM_NUM	(1)
#define ALM_FLOOR_TYPE_NUM	(4)
#define FLOOR_SLOT_NUM	(3)

#define ARC_MLD_BASE	(4)

typedef struct FLOOR_GRA_tag
{
	FLD_3DOBJ_ANM	Anm;
	FLD_3DOBJ		Obj;
}FLOOR_GRA;

//床データ
typedef struct ALM_FLOOR_tag
{
	BOOL Valid;
	int Type;
	FLOOR_GRA Gra[ALM_FLOOR_TYPE_NUM];
	TCB_PTR Tcb;
}ALM_FLOOR;

//スタックデータ
typedef struct ALM_FLOOR_STACKtag
{
	int SlotIdx;
	ALM_FLOOR GraDat[FLOOR_SLOT_NUM];
}ALM_FLOOR_STACK;

typedef struct ALM_GYM_TEMP_WORK_tag
{
	NNSFndAllocator Allocator;
	FLD_3DOBJ_MDL	FloorMdl[ALM_FLOOR_TYPE_NUM];
	ALM_FLOOR_STACK FloorStack;

}ALM_GYM_TEMP_WORK;

//グラフィックデータに依存する列挙型
enum {
	MV_LEFT_EFF,
	MV_DOWN_EFF,
	MV_RIGHT_EFF,
	MV_UP_EFF,
};

static void SetupGraphic(ALM_GYM_TEMP_WORK *work, const int inHeapID);
static void DelGraphic(ALM_GYM_TEMP_WORK *work);

static void SetFloorTcb(ALM_FLOOR *floor, FIELDSYS_WORK *fsys, const u8 inAttr);
static void EffTcb( TCB_PTR tcb, void *work );
static BOOL AnimeNoLoop(FLD_3DOBJ_ANM *anm);

//---------------------------------------------------------------------------
/**
 * @brief	万能ジム:セットアップ
 *
 * @param	fsys	フィールドシステムポインタ
 *
 * @return	none
 */
//---------------------------------------------------------------------------
void GYM_SetupAlmGym(FIELDSYS_WORK *fsys)
{
	int i;
	GIMMICKWORK *work;
	ALMIGHTY_GYM_WORK *alm_gym_work;
	ALM_GYM_TEMP_WORK *alm_temp;

	OS_Printf("万能ジムセットアップ\n");

	//ここでギミックワークの中身を見る
	work = SaveData_GetGimmickWork(GameSystem_GetSaveData(fsys));
	alm_gym_work = (ALMIGHTY_GYM_WORK*)GIMMICKWORK_Get(work, FLD_GIMMICK_ALMIGHTY_GYM);

	//万能ジム専用ワークをアロケートして、フィールドマップ汎用ワークポインタにあてる
	fsys->fldmap->Work = sys_AllocMemory(HEAPID_FIELD, sizeof(ALM_GYM_TEMP_WORK));
	//初期化
	MI_CpuClear8(fsys->fldmap->Work, sizeof(ALM_GYM_TEMP_WORK));
	alm_temp = fsys->fldmap->Work;

	//使用する全床パーツをセットアップ
	SetupGraphic(alm_temp, HEAPID_FIELD);
}

//---------------------------------------------------------------------------
/**
 * @brief	万能ジム:終了処理
 *
 * @param	fsys	フィールドシステムポインタ
 *
 * @return	none
 */
//---------------------------------------------------------------------------
void GYM_EndAlmGym(FIELDSYS_WORK *fsys)
{
	u8 i;
	ALM_GYM_TEMP_WORK *alm_temp;
	alm_temp = (ALM_GYM_TEMP_WORK *)fsys->fldmap->Work;

	//グラフィックデータ削除
	DelGraphic(alm_temp);

	//TCBが起動している場合は削除する
	for (i=0;i<FLOOR_SLOT_NUM;i++){
		if ( alm_temp->FloorStack.GraDat[i].Valid ){
			OS_Printf("TCB削除\n");
			TCB_Delete(alm_temp->FloorStack.GraDat[i].Tcb);
		}
	}
	sys_FreeMemoryEz(alm_temp);
	fsys->fldmap->Work = NULL;
}

//---------------------------------------------------------------------------
/**
 * @brief	万能ジム:床踏んだときのエフェクト発生処理
 *
 * @param	fsys	フィールドシステムポインタ
 * @param	inAttr	アトリビュート
 *
 * @return	none
 */
//---------------------------------------------------------------------------
void GYM_SetAlmGymEff(FIELDSYS_WORK *fsys, const u8 inAttr)
{
	ALM_GYM_TEMP_WORK *alm_temp;
	ALM_FLOOR *floor;
	alm_temp = (ALM_GYM_TEMP_WORK *)fsys->fldmap->Work;

	//参照するスロットのデータを取得
	floor = &alm_temp->FloorStack.GraDat[alm_temp->FloorStack.SlotIdx];

	//スロット使用中で無ければ、新規エントリ
	if (!floor->Valid){
		SetFloorTcb(floor, fsys, inAttr);
		alm_temp->FloorStack.SlotIdx = (alm_temp->FloorStack.SlotIdx+1)%FLOOR_SLOT_NUM;
	}else{
		GF_ASSERT_MSG(0,"スロットが足りません\n");
	}
}

//---------------------------------------------------------------------------
/**
 * @brief	グラフィックセットアップ
 *
 * @param	work			ワーク
 * @param	inHeapID		ヒープID
 *
 * @return	none
 */
//---------------------------------------------------------------------------
static void SetupGraphic(ALM_GYM_TEMP_WORK *work, const int inHeapID)
{
	u8 i,s,t;
	// アロケーター作成
	sys_InitAllocator( &work->Allocator, inHeapID, 32 );

	//モデルロード
	for (i=0;i<ALM_FLOOR_TYPE_NUM;i++){
		FLD_3DObjMdlLoad(&work->FloorMdl[i], ARC_GYM_ALM_GRA , ARC_MLD_BASE+i, inHeapID);
	}

	//スロット分ループ
	for (s=0;s<FLOOR_SLOT_NUM;s++){
		ALM_FLOOR *floor;
		work->FloorStack.SlotIdx = 0;		//参照開始スロットインデックス初期化
		floor = &work->FloorStack.GraDat[s];
		floor->Valid = FALSE;
		floor->Type = 0;
		for (t=0;t<ALM_FLOOR_TYPE_NUM;t++){
			//アニメデータロード
			FLD_3DObjAnmLoad(	&floor->Gra[t].Anm, &work->FloorMdl[t], ARC_GYM_ALM_GRA,
								t,
								inHeapID, &work->Allocator );
			//描画オブジェクト初期化
			FLD_3DObjInit( &floor->Gra[t].Obj, &work->FloorMdl[t] );
			//アニメをリンク
			FLD_3DObjAddAnm( &floor->Gra[t].Obj, &floor->Gra[t].Anm );
			//フレームセット
			FLD_3DObjAnmSet( &floor->Gra[t].Anm, 0 );
			//座標セット
			{
				VecFx32 vec = {0,0,0};
				FLD_3DObjSetMatrix( &floor->Gra[s].Obj, vec.x, vec.y, vec.z );
			}
			//描画フラグ設定	(非表示)
			FLD_3DObjSetDraw( &floor->Gra[t].Obj, FALSE );
		}
	}
}

//---------------------------------------------------------------------------
/**
 * @brief	グラフィック削除
 *
 * @param	work		ワーク
 *
 * @return	none
 */
//---------------------------------------------------------------------------
static void DelGraphic(ALM_GYM_TEMP_WORK *work)
{
	u8 i,s,t;

	for (i=0;i<ALM_FLOOR_TYPE_NUM;i++){
		//モデルデータ破棄
		FLD_3DObjMdlDelete( &work->FloorMdl[i]);
	}

	for (s=0;s<FLOOR_SLOT_NUM;s++){
		ALM_FLOOR *floor;
		floor = &work->FloorStack.GraDat[s];
		for(t=0;t<ALM_FLOOR_TYPE_NUM;t++){
			//アニメデータ破棄
			FLD_3DObjAnmDelete( &floor->Gra[t].Anm, &work->Allocator);
		}
	}
}

//---------------------------------------------------------------------------
/**
 * @brief	TCBを新規セット
 *
 * @param	floor		床データポインタ
 * @param	fsys		フィールドシステムポインタ
 * @param	inAttr		アトリビュート
 *
 * @return	none
 */
//---------------------------------------------------------------------------
static void SetFloorTcb(ALM_FLOOR *floor, FIELDSYS_WORK *fsys, const u8 inAttr)
{
	u8 type;
	floor->Valid = TRUE;
	switch(inAttr){
	case MATTR_IDO_01:	//右
		type = MV_RIGHT_EFF;
		break;
	case MATTR_IDO_02:	//左
		type = MV_LEFT_EFF;
		break;
	case MATTR_IDO_03:	//上
		type = MV_UP_EFF;
		break;
	case MATTR_IDO_04:	//下
		type = MV_DOWN_EFF;
		break;
	default:
		GF_ASSERT(0);
		return;
	}

	floor->Type = type;
	floor->Tcb = TCB_Add(EffTcb, floor, 0);
	//壁表示
	FLD_3DObjSetDraw( &floor->Gra[type].Obj, TRUE );
	//壁表示座標をセット
	{
		VecFx32 vec = {0,0,0};
		FieldOBJ_VecPosGet( Player_FieldOBJGet( fsys->player ), &vec );
		FLD_3DObjSetMatrix( &floor->Gra[type].Obj, vec.x, vec.y, vec.z );
	}
	//フレームセット
	FLD_3DObjAnmSet( &floor->Gra[type].Anm, 0 );
}

//---------------------------------------------------------------------------
/**
 * @brief	アニメTCB
 *
 * @param	tcb		TCBポインタ
 * @param	work	ワークポインタ
 *
 * @return	none
 */
//---------------------------------------------------------------------------
static void EffTcb( TCB_PTR tcb, void *work )
{
	u8 type;
	ALM_FLOOR *floor = (ALM_FLOOR*)work;

	type = floor->Type;

	if ( AnimeNoLoop( &floor->Gra[type].Anm ) ){
		OS_Printf("TCB終了\n");
		//対象モデルをバニッシュ
		FLD_3DObjSetDraw( &floor->Gra[type].Obj, FALSE );
		//未使用状態にする
		floor->Valid = FALSE;
		//TCBを削除
		TCB_Delete(floor->Tcb);
		floor->Tcb = NULL;
		return;
	}

	FLD_3DObjDraw( &floor->Gra[type].Obj );
}

//==============================================================================
/**
 * アニメフレーム更新関数
 *
 * @param	anm			アニメポインタ配列
 *
 * @retval  BOOL		TRUE：アニメ終了	FALSE：アニメ継続
 */
//==============================================================================
static BOOL AnimeNoLoop(FLD_3DOBJ_ANM *anm)
{
	BOOL end;
	end = FLD_3DObjAnmNoLoop( anm, FX32_ONE );
	if (end){
		OS_Printf("%d:アニメ終了\n");
		return TRUE;
	}
	return FALSE;
}

